package com.gosuncn.FileCopy;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import redis.clients.jedis.Jedis;

import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * HttpClients+多线程实现跨服务器上传文件 通过redis进行缓存
 */
public class CallableTest1 {

    private static String serverUrl = "http://localhost:8081/file/fileupload";


    public String UploadFileByHttpClient(List<File> fileList,int fileNumber){
        //计数器
        CountDownLatch latch = new CountDownLatch(fileNumber);
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                4, //线程池大小  根据你的cpu核数最大
                4, //最大核心线程 根据你当前cpu核数确定
                13, //超时等待
                TimeUnit.SECONDS, //单位
                new LinkedBlockingDeque<>(Integer.MAX_VALUE), //阻塞队列
                Executors.defaultThreadFactory()/*, //线程工厂
                new ThreadPoolExecutor.DiscardPolicy()*/); //队列满了，尝试与第一个线程竞争，不会抛出异

        //连接redis
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        //获取redis缓存列表中的数据
        List<String> folderList = jedis.lrange("fileName", 0, -1);
        //开始时间
        long start = System.currentTimeMillis();
        for (File file : fileList) {
            //当前文件的绝对路径+相对路径
            String filePath = file.getAbsolutePath();
            // D:\oldpath\zxl\123.txt 获取给定目录下的所有文件目录 zxl\123.txt
            String folder = filePath.substring(filePath.indexOf("\\", 3)+1);
            //判断字符串是否在列表里面  存在返回true   必须要组装数据，在folderList=[""zxl\\ceshi.txt"",""ttt.txt"",""数据\\zxlsd.txt""]
            if (!folderList.contains("\""+folder.replace("\\","\\\\")+"\"")){
                pool.execute(new MyCallableConsumer1(serverUrl,folder,file,latch));

            }else {
                //当目标服务器存在文件时也要进行减1
                latch.countDown();
                System.out.println("目标服务器已存在"+folder+"文件");
            }
        }


        try {
            //计数器归零后再执行以下任务，不然在多并发下，有些线程提前完成后就关闭连接池，后续的线程就无法执行
            latch.await();
            long end = System.currentTimeMillis();
            System.out.println("-------------文件读、写任务所用时间："+(end-start));
            System.out.println("-----------所有的文件都已经同步成功-------------");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭连接池
            pool.shutdown();
            //定期清除缓存
            jedis.expire("fileName",60);
        }

        return "上传成功";



    }





    public class MyCallableConsumer1 implements Runnable{

        private String folder = "";
        private List<String> folderList = null;
        private File file;
        private String url = "";
        private CloseableHttpResponse response = null;
        private CountDownLatch latch;


        //用Multipart进行文件的上传  HttpMultipartMode.RFC6532防止文件乱码
        MultipartEntityBuilder builder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
        CloseableHttpClient httpClient = HttpClients.createDefault();

        public MyCallableConsumer1(String url,String folder,File file,CountDownLatch latch){
            this.url = url;
            this.folder = folder;
            this.file = file;
            this.latch = latch;
        }



        @Override
        public void run() {

            try {
                //通过post请求
                HttpPost httpPost = new HttpPost(url);
                //二进制文件
                builder.addBinaryBody("fileName",file, ContentType.MULTIPART_FORM_DATA,folder);

                //防止传递参数中文乱码
                StringBody folder1 = new StringBody(folder, ContentType.create("text/plain", Consts.UTF_8));
                builder.addPart("name",folder1);
                //设置参数，后台可以通过String params1 绑定入params1赋值（会中文乱码）
//                    builder.addTextBody("name",folder);
                //设置编码
                builder.setCharset(Consts.UTF_8);

                HttpEntity entity = builder.build();
                //设置请求参数
                httpPost.setEntity(entity);
                //提交响应
                response = httpClient.execute(httpPost);
                //接收调用外部接口响应的内容
                Header[] headers = response.getHeaders("token");
                for (Header header : headers) {
                    System.out.println("响应的状态码： "+response.getStatusLine().getStatusCode());
                    System.out.println(URLDecoder.decode(String.valueOf(header),"UTF-8"));
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //计数器减1
                latch.countDown();
            }
        }
    }


    public static void main(String[] args) {

        String folder = "D:\\dirA";
        CallableTest1 callableTest1 = new CallableTest1();
        File file1 = new File(folder);
        FileUtils fileController = new FileUtils(file1);
        //所有的文件
        ArrayList<File> allFileByFolder = fileController.getAllFile();
        //获取所有的文件数
        int filenumber = allFileByFolder.size();
        //上传文件
        callableTest1.UploadFileByHttpClient(allFileByFolder,filenumber);
    }
}
